package com.bigknow.appstore.proxy.handler.filter;

import com.bigknow.appstore.proxy.cache.ICacheManager;
import com.bigknow.appstore.proxy.entity.SecurityMeta;
import com.bigknow.appstore.proxy.enumerate.ErrorType;
import com.bigknow.appstore.proxy.enumerate.SecurityType;
import com.bigknow.appstore.proxy.exceptions.ProxyError;
import com.bigknow.appstore.proxy.exceptions.processor.impl.ErrorHelper;
import com.bigknow.appstore.proxy.handler.base.BaseInboundHandler;
import com.bigknow.appstore.proxy.log.ILogWriter;
import com.bigknow.appstore.proxy.server.IProxyServer;
import com.bigknow.appstore.proxy.util.HandlerVariables;
import com.bigknow.appstore.proxy.vo.ClientErrorCallTimesVo;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpMessage;

import java.net.InetSocketAddress;
import java.util.Date;

/**
 * 对请求进行拦截判断的handler,此handler放在uri handler后面（可以记录requestVo）
 * Created by chaos on 2016/9/27.
 */
@ChannelHandler.Sharable
public class RequestProxyErrorFilterHandler extends BaseInboundHandler {

    public RequestProxyErrorFilterHandler(IProxyServer proxyServer, ICacheManager cacheManager, ILogWriter writer) {
        super(proxyServer, cacheManager, writer);
    }

    @Override
    public void read(ChannelHandlerContext ctx, FullHttpMessage msg) throws Exception {
        SecurityMeta securityMeta = getSecurityMeta();
        if (securityMeta == null) {
            ctx.fireChannelRead(msg);
        } else {
            doFilter(ctx, securityMeta, msg);
        }
    }


    /**
     * 首先判断client是否是已经被断开了的连接，如果是并且没有过不能访问的时间，就直接断开连接
     * 如果是断开的连接，但是已经过了不能访问的时间，就remover记录。
     *
     * @param ctx
     * @param securityMeta
     */
    private void doFilter(ChannelHandlerContext ctx, SecurityMeta securityMeta, FullHttpMessage msg) {
        try {
            String clientAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress();
            int disConnectTimeout = Integer.parseInt(securityMeta.getPolicyAttrs().get("disConnectTimeout"));
            int errorTimes = Integer.parseInt(securityMeta.getPolicyAttrs().get("errorTimes"));

            //发生错误
            if (HandlerVariables.clientErrorCallTimesMap.containsKey(clientAddress)) {
                ClientErrorCallTimesVo vo = HandlerVariables.clientErrorCallTimesMap.get(clientAddress);
                if (vo.getDisConnectOccur().get()) {//已经被断开限制连接
                    Date disConnectTime = vo.getDisConnectOccurTime();
                    if (new Date().getTime() - disConnectTime.getTime() > disConnectTimeout) {
                        //超时时间已过，解除封印
                        HandlerVariables.clientErrorCallTimesMap.remove(clientAddress);
                        ctx.fireChannelRead(msg);
                    } else {
/*                        writer.writeLog(writer.createErrorLogObject(clientAddress, ErrorType.CALL_TO_MANY_ERROR_CONNECT
                                , "to many error connection"));*/
                        writer.writeLog(writer.createErrorLogObject(getCurrentRequestVo(), ErrorType.CALL_TO_MANY_ERROR_CONNECT
                                , "to many error connection"));
                        ctx.channel().close().awaitUninterruptibly();
                    }
                } else {
                    /**
                     * 如果没有被限制连接，那么就判断vo是否达到限制标准
                     * ,如果没有达到限制标准，就让他访问
                     */

                    if (vo.getErrorCount() >= errorTimes) {
                        vo.getDisConnectOccur().set(true);
                        vo.setDisConnectOccurTime(new Date());
                        /*
                        由于这是第一次发生错误，抛出ProxyError来给client看，第二次再来就
                        没这个界面了，直接断开连接
                        */
                        ErrorHelper.throwCallToManyErrorConnectError(ctx.channel()
                                , null
                                , disConnectTimeout);
                    }else{
                        ctx.fireChannelRead(msg);
                    }
                }
            }
            //没有发生错误
            else {
                ctx.fireChannelRead(msg);
            }
        } catch (ProxyError proxyError) {
            throw proxyError;
        } catch (Exception e) {//如果securityMeta乱搞，有可能会出错，这种错误就不管了
            e.printStackTrace();
            ctx.fireChannelRead(msg);
        }
    }

    private SecurityMeta getSecurityMeta() {
        return proxyServer.getProxyMeta().getSecMaps().get(SecurityType.CALL_ERRORS_FILTER_SEC);
    }
}
